package com.stb.shared.util;
import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * Thread safe version of SimpleDateFormat.
 * 
 * SimpleDateFormat objects are created on-demand, one per thread per format.
 * 
 * An initial one is created for the calling thread in order to validate the format.
 */
@SuppressWarnings("serial")
public class ThreadSafeSimpleDateFormat extends DateFormat 
{
	private static final ThreadLocalMap<String, DateFormat> formats = new ThreadLocalMap<String, DateFormat>();

	private final String pattern;
	private final int dateStyle;
	private final int timeStyle;
	private final Locale locale;
	
	/**
     * @param pattern the pattern describing the date and time format
     * @exception NullPointerException if the given pattern is null
     * @exception IllegalArgumentException if the given pattern is invalid
	 */
	public ThreadSafeSimpleDateFormat(String pattern) {
		if (pattern == null) {
			throw new IllegalArgumentException("Pattern cannot be null");
		}
		this.pattern = pattern;
		this.timeStyle = -1;
		this.dateStyle = -1;
		this.locale = null;
		this.getFormat();
	}
	
	/**
	 * @param dateStyle
	 * @param timeStyle
	 * @param locale
	 * @See DateFormat for styles
	 */
	public ThreadSafeSimpleDateFormat(int dateStyle, int timeStyle, Locale locale) {
		this.pattern = null;
		this.dateStyle = dateStyle;
		this.timeStyle = timeStyle;
		this.locale = locale;
		this.getFormat();
	}

	/**
	 * @param dateStyle
	 * @param locale
	 * @See DateFormat for styles
	 */
	public ThreadSafeSimpleDateFormat(int dateStyle, Locale locale) {
		this(dateStyle, -1, locale);
	}

	public String getPattern() {
		return pattern;
	}

	private DateFormat getFormat() {
		if (this.pattern != null) {
			return formats.get(this.pattern, new ThreadLocalMap.CreateCallback<String, DateFormat>() {
				@Override
				public SimpleDateFormat create(String pattern) {
					return new SimpleDateFormat(pattern);
				}
			});
		} else {
			String key = new StringBuilder().append(dateStyle).append(timeStyle).append(locale).toString();
			return formats.get(key, new ThreadLocalMap.CreateCallback<String, DateFormat>() {
				@Override
				public DateFormat create(String key) {
					if (dateStyle != -1 && timeStyle != -1) {
						return DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
					} else if (dateStyle == -1 && timeStyle != -1) {
						return DateFormat.getTimeInstance(timeStyle, locale);						
					} else if (timeStyle == -1 && dateStyle != -1) {
						return DateFormat.getDateInstance(dateStyle, locale);						
					} else {
						throw new IllegalArgumentException("Missing implementation for specified style combination: dateStyle: " + dateStyle + ", timeStyle: " + timeStyle);
					}
				}
			});
		}
	}

	@Override
	public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
		return this.getFormat().format(date, toAppendTo, fieldPosition);
	}

	@Override
	public Date parse(String source, ParsePosition pos) {
		return this.getFormat().parse(source, pos);
	}
}
